home *** CD-ROM | disk | FTP | other *** search
/ Games of Daze / Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso / x2ftp / msdos / progsrc / microcad / microcad.c < prev    next >
C/C++ Source or Header  |  1994-02-16  |  30KB  |  1,338 lines

  1. /*
  2.  * MICROCAD: One of the worlds smallest drawing programs.
  3.  *
  4.  * Copyright 1992-1993 Dave Dunfield
  5.  * All rights reserved.
  6.  *
  7.  * Permission granted for personal (non-commercial) use only.
  8.  *
  9.  * Compile command: cc microcad -fop
  10.  */
  11. #include <stdio.h>
  12.  
  13. /* Screen dimensions */
  14. #define    VERTICAL        480        /* Maximum screen height */
  15. #define    HORIZONTAL        640        /* Maximum screen width */
  16.  
  17. #define    ARC_RES            64        /* Resolution of each ARC quadrant */
  18.  
  19. /* Pixel colors */
  20. #define    SET_PIXEL        0x0F    /* Pixel will be set ON */
  21. #define    CLEAR_PIXEL        0x00    /* Pixel will be set OFF */
  22. #define    FLIP_PIXEL        0x8F    /* Pixel will be toggled ON/OFF */
  23.  
  24. /* Mouse buttons */
  25. #define    MOUSE_LEFT        0x01    /* Select button */
  26. #define    MOUSE_RIGHT        0x02    /* Cancel button */
  27. #define    MOUSE_CENTER    0x04    /* Not used in MICROCAD */
  28.  
  29. /* Drawing buffer codes */
  30. #define    LINE            0x01    /* Object is a LINE */
  31. #define    BOX                0x02    /* Object is a BOX */
  32. #define    CIRCLE            0x03    /* Object is a CIRCLE */
  33. #define    TEXT            0x04    /* Object is a TEXT string */
  34. #define    ARC                0x05    /* Object is an ARC */
  35. #define    GROUP            0x06    /* Object is a GROUP of objects */
  36. #define    ACOPY            0x07    /* Copy an object (absolute) */
  37. #define    RCOPY            0x08    /* Copy an object (relative) */
  38.  
  39. /* Drawing storage variables */
  40. unsigned char drawing[32000];    /* Drawing buffer */
  41. unsigned dtop = 0, dpos;        /* Extent and position */
  42.  
  43. /*
  44.  * Drawing parameters (saved with drawing) MUST be initialized!
  45.  *
  46.  * Note: In MICRO-C, we are guaranteed that initialized variables which
  47.  * are declared together will be grouped together in storage. For other
  48.  * compilers, you should read/write these separately to the file.
  49.  */
  50. #define    PBYTES    11                /* Size of parameter storage */
  51. int grid = 0,                    /* Grid spacing (0 = disable) */
  52.     snap = 0,                    /* Snap spacing (0 = disable) */
  53.     tscale = 100,                /* Text scale (100 = 1:1) */
  54.     xbase = 0,                    /* Cursor display base COLUMN */
  55.     ybase = 0;                    /* Cursor display base ROW */
  56. char tick = 1;                    /* Position marker flag */
  57.  
  58. /* Misc. variables */
  59. char pixel = SET_PIXEL,            /* Current video attribute for drawing */
  60.     cursor_flag = 0,            /* Indicate cursor is on-screen */
  61.     edit = -1,                    /* Indicates EDIT mode */
  62.     font[4608],                    /* Text font storage */
  63.     dfile[65],                    /* Drawing file name */
  64.     ffile[65],                    /* Font file name */
  65.     ifile[65],                    /* Insert file name */
  66.     vmode;                        /* Saved video mode */
  67.  
  68. unsigned mousex = -1, mousey = -1;    /* Cursor X/Y position */
  69.  
  70. /* 
  71.  * Sine table for drawing ARC's
  72.  */
  73. unsigned sine[] = {
  74.         0,  1608,  3216,  4821,  6424,  8022,  9616, 11204,
  75.     12785, 14359, 15924, 17479, 19024, 20557, 22078, 23586,
  76.     25079, 26557, 28020, 29465, 30893, 32302, 33692, 35062,
  77.     36410, 37736, 39040, 40320, 41575, 42806, 44011, 45190,
  78.     46341, 47464, 48559, 49624, 50660, 51665, 52639, 53581,
  79.     54491, 55368, 56212, 57022, 57797, 58538, 59243, 59913,
  80.     60547, 61144, 61705, 62228, 62714, 63162, 63571, 63943,
  81.     64276, 64571, 64826, 65043, 65220, 65358, 65457, 65516 };
  82.  
  83. extern FILE *get_file();    /* Prototype for FILE function */
  84.  
  85. /*
  86.  * Main program - process commands at the highest level
  87.  */
  88. main(argc, argv)
  89.     int argc;
  90.     char *argv[];
  91. {
  92.     int i, j, select;
  93.     char *ptr;
  94.     FILE *fp;
  95.  
  96.     /* Set default file names */
  97.     concat(dfile, "MICROCAD.DWG");
  98.     concat(ffile, "MICROCAD.FNT");
  99.     *ifile = 0;
  100.  
  101.     /* Parse command line arguments */
  102.     for(i=1; i < argc; ++i) {
  103.         ptr = argv[i];
  104.         switch((toupper(*ptr++) << 8) | toupper(*ptr++)) {
  105.             case 'F=' :        /* Specify alternate FONT file */
  106.                 concat(ffile, ptr, ".FNT");
  107.                 break;
  108.             case '/D' :        /* Display only mode */
  109.             case '-D' :
  110.                 edit = 0;
  111.                 break;
  112.             case '?' << 8:    /* Command line help */
  113.             case '/?' :
  114.             case '-?' :
  115.                 abort("\nUse: MICROCAD [drawing file] [F=font file] [/Display]\n\nCopyright 1992-1993 Dave Dunfield\nAll rights reserved.\n");
  116.             default:        /* Assume drawing filename */
  117.                 concat(dfile, argv[i], ".DWG"); } }
  118.  
  119.  
  120.     /* Read in "font" file */
  121.     fp = fopen(ffile, "qrbv");
  122.     fread(font, sizeof(font), fp);
  123.     fclose(fp);
  124.  
  125.     /* Read in the drawing file */
  126.     if(fp = fopen(dfile, "rbv")) {
  127.         fread(&grid, PBYTES, fp);
  128.         dtop = fread(drawing, sizeof(drawing), fp);
  129.         fclose(fp); }
  130.  
  131.     /* Zero unused portion of drawing file */
  132.     zero_drawing(dtop);
  133.  
  134.     /* Initialize video adapter, and test for VGA */
  135.     if(!init_video())
  136.         abort("VGA required!");
  137.  
  138.     /* If not edit... Insure grid & tick off, display & exit */
  139.     if(!edit) {
  140.         grid = 0;
  141.         tick = 1;
  142.         redraw();
  143.         get_key();
  144.         video_mode(vmode);
  145.         return; }
  146.  
  147.     /* Initialize mouse */
  148.     if(!init_mouse())
  149.         abort("MOUSE required!");
  150.  
  151.     /* Clear screen & draw current drawing */
  152.     redraw();
  153.  
  154.     if(!fp) {
  155.         message();
  156.         printf("New drawing: '%s'", dfile); }
  157.  
  158.     /* Process commands */
  159.     for(;;) {
  160.         if((i=mouse_status()) & MOUSE_LEFT) {    /* Prompt for new command */
  161.             message();
  162.             printf("A)rc B)ox C)ircle D)up E)rase F)unc L)ine M)ove R)edraw S)etup T)ext U)ndo");
  163.             select = get_key(); }
  164.         else if(j = test_key())
  165.             select = j;
  166.         else if(!(i & MOUSE_RIGHT))                /* Repeat last command */
  167.             continue;
  168.         /* Perform command operation */
  169.         switch(toupper(select)) {
  170.             case 'A' :        /* Draw an ARC */
  171.                 draw_arc();
  172.                 break;
  173.             case 'B' :        /* Draw BOX */
  174.                 draw_box();
  175.                 break;
  176.             case 'C' :        /* Draw CIRCLE */
  177.                 draw_circle();
  178.                 break;
  179.             case 'D' :        /* Duplicate object */
  180.                 copy();
  181.                 break;
  182.             case 'E' :        /* Erase object */
  183.                 erase();
  184.                 break;
  185.             case 'F' :        /* Function menu */
  186.                 function();
  187.                 break;
  188.             case 'L' :        /* Draw LINE's */
  189.                 draw_line();
  190.                 break;
  191.             case 'M' :        /* Move object */
  192.                 move();
  193.                 break;
  194.             case 'R' :        /* Redraw the drawing */
  195.                 redraw();
  196.                 break;
  197.             case 'T' :        /* Text entry */
  198.                 draw_text();
  199.                 break;
  200.             case 'U' :        /* Erase last object from list */
  201.                 undo();
  202.                 break;
  203.             case 'S' :        /* Settings */
  204.                 message();
  205.                 printf("B)ase-markers C)ursor-base G)rid S)nap T)ext-scale");
  206.                 switch(toupper(get_key())) {
  207.                     case 'B' :        /* Basepoint markers */
  208.                         tick = tick ? 0 : 1;
  209.                         redraw();
  210.                         continue;
  211.                     case 'C' :        /* Cursor base address */
  212.                         i = xbase;
  213.                         j = ybase;
  214.                         xbase = ybase = 0;
  215.                         if(!wait_for_left("Select cursor base point")) {
  216.                             xbase = mousex;
  217.                             ybase = mousey;
  218.                             goto exit; }
  219.                         xbase = i;
  220.                         ybase = j;
  221.                         continue;
  222.                     case 'G' :        /* Set/Remove GRID */
  223.                         grid = get_value("Grid spacing (0 to remove)?");
  224.                         redraw();
  225.                         continue;
  226.                     case 'S' :        /* Set/Remove SNAP */
  227.                         snap = get_value("Snap spacing (0 to remove)?");
  228.                         goto exit;
  229.                     case 'T' :        /* Text scale */
  230.                         tscale = get_value("Text scale (100 = 1:1)?");
  231.                     exit:
  232.                         message();
  233.                         continue; }
  234.             default:
  235.                 message(); 
  236.                 printf("Unknown command!"); } }
  237. }
  238.  
  239. /*
  240.  * Perform misc. functions not on main menu.
  241.  */
  242. function()
  243. {
  244.     FILE *fp;
  245.     
  246.     message();
  247.     printf("F)ont I)nsert L)oad Q)uit S)ave");
  248.     switch(toupper(get_key())) {
  249.         case 'I' :        /* Insert a drawing */
  250.             insert();
  251.             return;
  252.         case 'F' :        /* Load alternate FONT file */
  253.             if(fp = get_file("font", ffile, ".FNT", "rbv")) {
  254.                 fread(font, sizeof(font), fp);
  255.                 fclose(fp);
  256.                 redraw(); }
  257.             return;
  258.         case 'L' :        /* Load drawing file */
  259.             if(fp = get_file("drawing", dfile, ".DWG", "rbv")) {
  260.                 fread(&grid, PBYTES, fp);
  261.                 zero_drawing(fread(drawing, sizeof(drawing), fp));
  262.                 fclose(fp);
  263.                 redraw(); }
  264.             return;
  265.         case 'S' :        /* Save drawing file */
  266.             if(fp = get_file("drawing", dfile, ".DWG", "wbv")) {
  267.                 fwrite(&grid, PBYTES, fp);
  268.                 if(dtop)
  269.                     fwrite(drawing, dtop, fp);
  270.                 fclose(fp);
  271.                 message(); }
  272.             return;
  273.         case 'Q' :        /* Quit */
  274.             video_mode(vmode);
  275.             exit(0); }
  276.  
  277.         message();
  278.         printf("Unknown command!");
  279. }
  280.  
  281. /*
  282.  * Insert another drawing into this one as a GROUP
  283.  */
  284. insert()
  285. {
  286.     unsigned i, dsave, base, size, minx, miny, x, y;
  287.     char buffer[PBYTES];
  288.     FILE *fp;
  289.  
  290.     /* Get filename & insertion point */
  291.     if(!(fp = get_file("drawing", ifile, ".DWG", "rbv")))
  292.         return;
  293.  
  294.     dsave = dtop;
  295.     drawing[dtop++] = GROUP;
  296.     draw(0);    /* Reserve 'X' */
  297.     draw(0);    /* Reserve 'Y' */
  298.     draw(0);    /* Reserve group size */
  299.  
  300.     /* Append the drawing objects */
  301.     fread(buffer, PBYTES, fp);
  302.     size = fread(&drawing[base = dpos = dtop], sizeof(drawing) - dtop, fp);
  303.     fclose(fp);
  304.  
  305.     /* Locate base point of group */
  306.     minx = miny = 32767;
  307.     while(drawing[dpos]) {
  308.         i = dpos++;
  309.         minx = min(x = dvalue(), minx);
  310.         miny = min(y = dvalue(), miny);
  311.         dpos = i;
  312.         skip_object(); }
  313.     
  314.     /* Reposition new drawing objects to relative offset from group base */
  315.     dpos = base;
  316.     while(x = drawing[i = dpos]) {
  317.         dtop = ++dpos;
  318.         draw(dvalue() - minx);
  319.         draw(dvalue() - miny);
  320.         /* Convert any absolute copy objects in the group relative copy
  321.          * objects, so that group is entirely position independant. */
  322.         if(x == ACOPY) {
  323.             drawing[i] = RCOPY;
  324.             draw((dvalue()+base) - i); }
  325.         dpos = i;
  326.         skip_object(); }
  327.  
  328.     /* Origin group at zero, and update size */
  329.     dtop = dsave + 1;
  330.     draw(0);
  331.     draw(0);
  332.     draw(size);
  333.  
  334.     /* Get selection point */
  335.     message();
  336.     printf("INSERT: Selection insertion point");
  337.     pixel = FLIP_PIXEL;
  338. newbox:
  339.     dpos = dsave;
  340.     draw_object(x = mousex, y=mousey);
  341.     while(!((i = mouse_status()) & MOUSE_LEFT)) {
  342.         if(i & MOUSE_RIGHT) {
  343.             dpos = dsave;
  344.             draw_object(x, y);
  345.             zero_drawing(dsave);
  346.             message();
  347.             return; }
  348.         if((x != mousex) || (y != mousey)) {
  349.             dpos = dsave;
  350.             draw_object(x, y);
  351.             goto newbox; } }
  352.  
  353.     /* Fix up pointers and redraw */
  354.     dpos = dsave;
  355.     pixel = SET_PIXEL;
  356.     dtop = dsave + 1;
  357.     draw(x);
  358.     draw(y);
  359.     dtop = base + size;
  360.     redraw();
  361. }
  362.  
  363. /*
  364.  * Position and draw one or more lines.
  365.  */
  366. draw_line()
  367. {
  368.     int x, y, x1, y1, s;
  369.     if(wait_for_left("LINE: From point?"))
  370.         return;
  371.     gotoxy(30, 0); printf(" To point?");
  372.     x = mousex;
  373.     y = mousey;
  374. again:
  375.     pixel = FLIP_PIXEL;
  376. newline:
  377.     line(x, y, x1 = mousex, y1 = mousey);
  378.     while(!((s = mouse_status()) & MOUSE_LEFT)) {
  379.         if(s & MOUSE_RIGHT) {
  380.             line(x, y, x1, y1);
  381.             pixel = SET_PIXEL;
  382.             message();
  383.             return; }
  384.         if((mousex != x1) || (mousey != y1)) {
  385.             line(x, y, x1, y1);
  386.             goto newline; } }
  387.     pixel = SET_PIXEL;
  388.     drawing[dpos = dtop++] = LINE;
  389.     draw(x);
  390.     draw(y);
  391.     draw(x1 - x);
  392.     draw(y1 - y);
  393.     draw_object(0, 0);
  394.     x = x1;
  395.     y = y1;
  396.     goto again;
  397. }
  398.  
  399. /*
  400.  * Position and draw a box
  401.  */
  402. draw_box()
  403. {
  404.     int x, y, x1, y1, s;
  405.     if(wait_for_left("BOX: First corner?"))
  406.         return;
  407.     gotoxy(30, 0); printf(" Second corner?");
  408.     x = mousex;
  409.     y = mousey;
  410.     pixel = FLIP_PIXEL;
  411. newbox:
  412.     box(x, y, x1 = mousex, y1 = mousey);
  413.     while(!((s = mouse_status()) & MOUSE_LEFT)) {
  414.         if(s & MOUSE_RIGHT) {
  415.             box(x, y, x1, y1);
  416.             message();
  417.             return; }
  418.         if((mousex != x1) || (mousey != y1)) {
  419.             box(x, y, x1, y1);
  420.             goto newbox; } }
  421.     pixel = SET_PIXEL;
  422.     message();
  423.     drawing[dpos = dtop++] = BOX;
  424.     draw(x);
  425.     draw(y);
  426.     draw(x1 - x);
  427.     draw(y1 - y);
  428.     draw_object(0, 0);
  429. }
  430.  
  431. /*
  432.  * Position and draw a circle
  433.  */
  434. draw_circle()
  435. {
  436.     int x, y, r, r1, a, b, s;
  437.     if(wait_for_left("CIRCLE: Select center point"))
  438.         return;
  439.     x = mousex;
  440.     y = mousey;
  441.     pixel = FLIP_PIXEL;
  442.     r = 1;
  443. newcircle:
  444.     circle(x, y, r);
  445.     while(!((s = mouse_status()) & MOUSE_LEFT)) {
  446.         if(s & MOUSE_RIGHT) {
  447.             circle(x, y, r);
  448.             message();
  449.             return; }
  450.         a = abs(mousex - x);
  451.         b = abs(mousey - y);
  452.         if((r1 = sqrt((a*a) + (b*b))) != r) {
  453.             gotoxy(40, 0); printf("Radius=%-3u", r1);
  454.             circle(x, y, r);
  455.             r = r1;
  456.             goto newcircle; } }
  457.     pixel = SET_PIXEL;
  458.     message();
  459.     drawing[dpos = dtop++] = CIRCLE;
  460.     draw(x);
  461.     draw(y);
  462.     draw(r);
  463.     draw_object(0, 0);
  464. }
  465.  
  466. /*
  467.  * Position and draw an arc
  468.  */
  469. draw_arc()
  470. {
  471.     int x, y, r, v1, v2, x1, y1, s;
  472.  
  473.     if(wait_for_left("ARC: Select center point"))
  474.         return;
  475.     x = mousex;
  476.     y = mousey;
  477.     if(wait_for_left("ARC: Select starting point"))
  478.         return;
  479.     x1 = abs(mousex - x);
  480.     y1 = abs(mousey - y);
  481.     r = sqrt((x1*x1) + (y1*y1));
  482.     v1 = find_vector(x, y, r, mousex, mousey);
  483.     message();
  484.     printf("ARC: Select ending point");
  485.     pixel = FLIP_PIXEL;
  486. newarc:
  487.     v2 = find_vector(x, y, r, x1=mousex, y1=mousey);
  488.     arc(x, y, r, v1, v2);
  489.     while(!((s = mouse_status()) & MOUSE_LEFT)) {
  490.         if(s & MOUSE_RIGHT) {
  491.             arc(x, y, r, v1, v2);
  492.             message();
  493.             return; }
  494.         if((mousex != x1) || (mousey != y1)) {
  495.             arc(x, y, r, v1, v2);
  496.             goto newarc; } }
  497.     pixel = SET_PIXEL;
  498.     message();
  499.     drawing[dpos = dtop++] = ARC;
  500.     draw(x);
  501.     draw(y);
  502.     draw(r);
  503.     draw(v1);
  504.     draw(v2);
  505.     draw_object(0, 0);
  506. }
  507.  
  508. /*
  509.  * Position and draw a text string
  510.  */
  511. draw_text()
  512. {
  513.     unsigned i, x, y;
  514.     char buffer[80], *ptr;
  515.  
  516.     message();
  517.     printf("String?");
  518.     fgets(ptr = buffer, sizeof(buffer)-1, stdin);
  519.     message();
  520.     if(*buffer) {
  521.         printf("TEXT: Select position");
  522.         pixel = FLIP_PIXEL;
  523.     newtext:
  524.         text(buffer, x = mousex, y = mousey, tscale);
  525.         while(!((i = mouse_status()) & MOUSE_LEFT)) {
  526.             if(i & MOUSE_RIGHT) {                    /* Cancel */
  527.                 text(buffer, x, y, tscale);
  528.                 pixel = SET_PIXEL;
  529.                 message();
  530.                 return; }
  531.             if((x != mousex) || (y != mousey)) {    /* Position changed */
  532.                 text(buffer, x, y, tscale);
  533.                 goto newtext; } }
  534.  
  535.         pixel = SET_PIXEL;
  536.         message();
  537.         drawing[dpos = dtop++] = TEXT;
  538.         draw(mousex);
  539.         draw(mousey);
  540.         draw(tscale);
  541.         do
  542.             drawing[dtop++] = *ptr;
  543.         while(*ptr++);
  544.         draw_object(0, 0); }
  545. }
  546.  
  547. /*
  548.  * Draw the graphic cursor on the screen at mouse position
  549.  */
  550. draw_cursor()
  551. {
  552.     char s;
  553.  
  554.     s = pixel;
  555.     pixel = FLIP_PIXEL;
  556.     line(max(0, mousex-3), mousey, min(HORIZONTAL-1,mousex+3), mousey);
  557.     line(mousex, max(0,mousey-3), mousex, min(VERTICAL-1,mousey+3));
  558.     pixel = s;
  559. }
  560.  
  561. /*
  562.  * Initialize the screen (clearing it), and display the drawing.
  563.  */
  564. redraw()
  565. {
  566.     int x, y;
  567.  
  568.     video_mode(0x11);
  569.     dpos = cursor_flag = 0;
  570.     pixel = SET_PIXEL;
  571.     while(!draw_object(0, 0));
  572.     if(grid) {
  573.         for(y = 0; y < VERTICAL; y += grid)
  574.             for(x=0; x < HORIZONTAL; x += grid)
  575.                 set_pixel(x, y); }
  576. }
  577.  
  578. /*
  579.  * Remove the last operation from the drawing list, and redraw
  580.  * it in "off" pixels, effectivly undoing the last operation.
  581.  */
  582. undo()
  583. {
  584.     unsigned dlast;
  585.     dpos = dlast = 0;
  586.     while(drawing[dpos]) {
  587.         dlast = dpos;
  588.         if(skip_object())
  589.             return; }
  590.     dpos = dlast;
  591.     pixel = CLEAR_PIXEL;
  592.     draw_object(0, 0);
  593.     pixel = SET_PIXEL;
  594.     zero_drawing(dlast);
  595.     message();
  596. }
  597.  
  598. /*
  599.  * Erase an object from the drawing file
  600.  */
  601. erase()
  602. {
  603.     unsigned e, d, i, x, y;
  604.  
  605.     if(select_object("ERASE")) {
  606.         d = dtop;
  607.         e = dpos;        /* Address of object to erase */
  608.  
  609.         /* Scan list, for copies of the object we are about to delete.
  610.          * If found, re-position this object to the address of its copy,
  611.           * and delete the copy object instead. */
  612.         while(i = drawing[dpos]) {
  613.             if(i == ACOPY) {
  614.                 i = dpos++;
  615.                 x = dvalue();
  616.                 y = dvalue();
  617.                 if(dvalue() == e) {    /* Move copy into object */
  618.                     dtop = e + 1;
  619.                     draw(x);
  620.                     draw(y);
  621.                     e = i; }
  622.                 dpos = i; }
  623.             skip_object(); }
  624.  
  625.         /* Scan list again, for copies referencing objects higher than the
  626.          * one we finally decided to delete, and adjust reference offset. */
  627.         dpos = e;
  628.         skip_object();
  629.         x = dpos - e;
  630.         while(i = drawing[dpos]) {
  631.             if(i == ACOPY) {
  632.                 i = dpos;
  633.                 dpos += 5;
  634.                 if((y = dvalue()) > e) {
  635.                     dtop = dpos - 2;
  636.                     draw(y - x); }
  637.                 dpos = i; }
  638.             skip_object(); }
  639.  
  640.     /* Remove the selected entry */
  641.         dpos = e;
  642.         skip_object();
  643.         while(dpos < d)
  644.             drawing[e++] = drawing[dpos++];
  645.         zero_drawing(e); }
  646. }
  647.  
  648. /*
  649.  * Moves an object to a new location
  650.  */
  651. move()
  652. {
  653.     unsigned psave, i, x, y, sx, sy;
  654.     if(select_object("MOVE")) {
  655.         /* Save position in file */
  656.         psave = dpos;
  657.         /* Save old position in case we cancel */
  658.         ++dpos;
  659.         sx = dvalue();
  660.         sy = dvalue();
  661.         message();
  662.         printf("MOVE: Select new position");
  663.         pixel = FLIP_PIXEL;
  664. newmove:
  665.         dpos = psave;
  666.         draw_object((x = mousex) - sx, (y = mousey) - sy);
  667.         while(!((i = mouse_status()) & MOUSE_LEFT)) {
  668.             if(i & MOUSE_RIGHT) {                    /* Cancel */
  669.                 dpos = psave;
  670.                 draw_object(x - sx, y - sy);
  671.                 goto skipmove; }
  672.             if((x != mousex) || (y != mousey)) {    /* Position changed */
  673.                 dpos = psave;
  674.                 draw_object(x - sx, y - sy);
  675.                 goto newmove; } }
  676.  
  677.         /* Update object with new location */
  678.         i = dtop;
  679.         dtop = psave + 1;
  680.         draw(x);
  681.         draw(y);
  682.         dtop = i;
  683.  
  684. skipmove:
  685.         /* Draw the object in its final resting place */
  686.         pixel = SET_PIXEL;
  687.         message();
  688.         dpos = psave;
  689.         draw_object(0, 0); }
  690. }
  691.  
  692. /*
  693.  * Copies an object to a new location
  694.  */
  695. copy()
  696. {
  697.     unsigned psave, i, x, y, sx, sy;
  698.  
  699.     if(select_object("DUP")) {
  700.         psave = dpos;
  701.         ++dpos;
  702.         sx = dvalue();
  703.         sy = dvalue();
  704.         pixel = SET_PIXEL;
  705.         dpos = psave;
  706.         draw_object(0, 0);
  707.         pixel = FLIP_PIXEL;
  708.         message();
  709.         printf("DUP: Select new position");
  710.     newcopy:
  711.         dpos = psave;
  712.         draw_object((x = mousex) - sx, (y = mousey) - sy);
  713.         while(!((i = mouse_status()) & MOUSE_LEFT)) {
  714.             if(i & MOUSE_RIGHT) {
  715.                 dpos = psave;
  716.                 draw_object(x - sx, y - sy);
  717.                 message();
  718.                 return; }
  719.             if((x != mousex) || (y != mousey)) {
  720.                 dpos = psave;
  721.                 draw_object(x - sx, y - sy);
  722.                 goto newcopy; } }
  723.  
  724.         /* Append a copy object pointing to original */
  725.         drawing[dpos = dtop++] = ACOPY;
  726.         draw(x);
  727.         draw(y);
  728.         draw(psave);
  729.  
  730.         message();
  731.         pixel = SET_PIXEL;
  732.         draw_object(0, 0); }
  733. }
  734.  
  735. /*
  736.  * Draws an object from the drawing file
  737.  */
  738. draw_object(xoffset, yoffset)
  739.     int xoffset, yoffset;
  740. {
  741.     int x, y, i, j, k, l;
  742.     char buffer[80], *ptr;
  743.  
  744.     if(cursor_flag)
  745.         mouse_status();
  746.  
  747.     if(!(i = drawing[dpos++]))
  748.         return -1;
  749.     x = dvalue() + xoffset;
  750.     y = dvalue() + yoffset;
  751.     if(!tick++)
  752.         box(max(0, x-1), max(0, y-1), min(HORIZONTAL-1, x+1), min(VERTICAL-1, y+1));
  753.  
  754.     switch(i) {
  755.         case LINE :
  756.             line(x, y, x+dvalue(), y+dvalue());
  757.             break;
  758.         case BOX :
  759.             box(x, y, x+dvalue(), y+dvalue());
  760.             break;
  761.         case CIRCLE :
  762.             circle(x, y, dvalue());
  763.             break;
  764.         case TEXT :
  765.             i = dvalue();
  766.             ptr = buffer;
  767.             do
  768.                 *ptr++ = j = drawing[dpos++];
  769.             while(j);
  770.             text(buffer, x, y, i);
  771.             break;
  772.         case ARC:
  773.             arc(x, y, dvalue(), dvalue(), dvalue());
  774.             break;
  775.         case GROUP :
  776.             i = dvalue();
  777.             j = dpos;
  778.             while((dpos - j) < i)
  779.                 draw_object(x, y);
  780.             break;
  781.         case RCOPY :
  782.             i = dpos - 5;
  783.             i += dvalue();
  784.             goto gocopy;
  785.         case ACOPY :
  786.             i = dvalue();
  787.         gocopy:
  788.             j = dpos;
  789.             dpos = i+1;
  790.             k = dvalue();
  791.             l = dvalue();
  792.             dpos = i;
  793.             draw_object(x - k, y - l);
  794.             dpos = j;
  795.             break;
  796.         default:
  797.             message();
  798.             printf("Corrupt drawing file!");
  799.             zero_drawing(dpos -= 5);
  800.             return -1; }
  801.     --tick;
  802.     return 0;
  803. }
  804.  
  805. /*
  806.  * Skip to next object in the drawing file
  807.  */
  808. skip_object()
  809. {
  810.     unsigned i;
  811.  
  812.     switch(drawing[dpos++]) {
  813.         case LINE :
  814.         case BOX :
  815.             dpos += 8;
  816.             break;
  817.         case CIRCLE :
  818.         case ACOPY :
  819.         case RCOPY :
  820.             dpos += 6;
  821.             break;
  822.         case TEXT :
  823.             dpos += 6;
  824.             while(drawing[dpos++]);
  825.             break;
  826.         case ARC :
  827.             dpos += 10;
  828.             break;
  829.         case GROUP :
  830.             dvalue();
  831.             dvalue();
  832.             i = dvalue();
  833.             dpos += i;
  834.             break;
  835.         default :
  836.             message();
  837.             printf("Corrupt drawing file!");
  838.             zero_drawing(--dpos);
  839.         case 0 :
  840.             return -1; }
  841.     return 0;
  842. }
  843.  
  844. /*
  845.  * Selects an object with the cursor
  846.  */
  847. select_object(name)
  848.     char *name;
  849. {
  850.     int x, y, i, b;
  851.     char flag;
  852.  
  853.     flag = -1;
  854.     for(;;) {
  855.         if(flag) {
  856.             message();
  857.             printf("%s: Select object", name);
  858.             flag = 0; }
  859.         if((b = mouse_status()) & MOUSE_RIGHT) {
  860.             message();
  861.             return 0; }
  862.         x = mousex;
  863.         y = mousey;
  864.         dpos = 0;
  865.         while(drawing[i = dpos++]) {
  866.             if((dvalue() == x) && (dvalue() == y)) {
  867.                 if(cursor_flag) {
  868.                     draw_cursor();
  869.                     cursor_flag = 0; }
  870.                 dpos = i;
  871.                 pixel = CLEAR_PIXEL;
  872.                 draw_object(0, 0);
  873.                 pixel = SET_PIXEL;
  874.                 if(wait_for_left("LEFT to accept, RIGHT to cancel")) {
  875.                     dpos = i;
  876.                     draw_object(0, 0);
  877.                     flag = -1;
  878.                     continue; }
  879.                 dpos = i;
  880.                 message();
  881.                 return -1; }
  882.             dpos = i;
  883.             if(skip_object())
  884.                 break; } }
  885. }
  886.  
  887. /*
  888.  * Find ARC vector pointing toward a certain point.
  889.  *
  890.  * Calculate the location of the point on the arc produced by each
  891.  * vector, and record the vector which brings us closest to the
  892.  * desired point.
  893.  */
  894. find_vector(x, y, r, sx, sy)
  895.     int x, y, r, sx, sy;
  896. {
  897.     int rs, i, j, ax, x1, y1, x2, y2, v, v1;
  898.     unsigned d, d1;
  899.  
  900.     rs = r*r;
  901.  
  902.     d1 = -1;
  903.     for(v=0; v < (ARC_RES*4); ++v) {
  904.         j = (ARC_RES-1) - (i = v & (ARC_RES-1));
  905.         switch(v & (ARC_RES*3)) {
  906.             case ARC_RES*0 :        /* Quadrant one */
  907.                 x1 = x + (ax = scale(r, sine[i], -1));
  908.                 y1 = y - sqrt(rs - (ax*ax));
  909.                 break;
  910.             case ARC_RES*1 :        /* Quadrant two */
  911.                 x1 = x + (ax = scale(r, sine[j], -1));
  912.                 y1 = y + sqrt(rs - (ax*ax));
  913.                 break;
  914.             case ARC_RES*2 :        /* Quadrant three */
  915.                 x1 = x - (ax = scale(r, sine[i], -1));
  916.                 y1 = y + sqrt(rs - (ax*ax));
  917.                 break;
  918.             case ARC_RES*3 :        /* Quadrant four */
  919.                 x1 = x - (ax = scale(r, sine[j], -1));
  920.                 y1 = y - sqrt(rs - (ax*ax)); }
  921.         x2 = abs(x1 - sx);
  922.         y2 = abs(y1 - sy);
  923.         if((d = sqrt((x2*x2) + (y2*y2))) < d1) {
  924.             v1 = v;
  925.             d1 = d; } }
  926.     return v1;
  927. }
  928.  
  929. /*
  930.  * Draw a line from point (x1, y1) to (x2, y2)
  931.  */
  932. line(x1, y1, x2, y2)
  933.     int x1, y1, x2, y2;
  934. {
  935.     int i, w, h;
  936.     /* If 'X' is greater, increment through 'X' coordinate */
  937.     if((w = abs(x1 - x2)) >= (h = abs(y1 - y2))) {
  938.         if(x1 > x2) {
  939.             i = x1;
  940.             x1 = x2;
  941.             x2 = i;
  942.             i = y1;
  943.             y1 = y2;
  944.             y2 = i; }
  945.         if(y1 < y2) {
  946.             for(i=0; i < w; ++i)
  947.                 set_pixel(x1+i, y1+scale(i, h, w)); }
  948.         else {
  949.             for(i=0; i < w; ++i)
  950.                 set_pixel(x1+i, y1-scale(i, h, w)); } }
  951.     /* If 'Y' is greater, increment through 'Y' coordinate */
  952.     else {
  953.         if(y1 > y2) {
  954.             i = x1;
  955.             x1 = x2;
  956.             x2 = i;
  957.             i = y1;
  958.             y1 = y2;
  959.             y2 = i; }
  960.         if(x1 < x2) {
  961.             for(i=0; i < h; ++i)
  962.                 set_pixel(x1+scale(i, w, h), y1+i); }
  963.         else {
  964.             for(i=0; i < h; ++i)
  965.                 set_pixel(x1-scale(i, w, h), y1+i); } }
  966.  
  967.     set_pixel(x2, y2);
  968. }
  969.  
  970. /*
  971.  * Draw a box with opposite corners (x1, y1) to (x2, y2)
  972.  */
  973. box(x1, y1, x2, y2)
  974.     int x1, y1, x2, y2;
  975. {
  976.     line(x1, y1, x2, y1);        /* Top */
  977.     line(x1, y1+1, x1, y2-1);    /* Left side */
  978.     line(x2, y1+1, x2, y2-1);    /* Right side */
  979.     line(x1, y2, x2, y2);        /* Bottom */
  980. }
  981.  
  982. /*
  983.  * Draw a circle about point (x, y) of radius (r)
  984.  *
  985.  * For speed, we only calculate 1 quadrant of the circle,
  986.  * and mirror it into the other three quadrants.
  987.  */
  988. circle(x, y, r)
  989.     int x, y, r;
  990. {
  991.     int i, j, k, rs, lj;
  992.  
  993.     rs = (lj = r)*r;
  994.     for(i=0; i <= r; ++i) {
  995.         j = k = sqrt(rs - (i*i));
  996.         do {
  997.             set_pixel(x+i, y+j);
  998.             set_pixel(x+i, y-j);
  999.             set_pixel(x-i, y+j);
  1000.             set_pixel(x-i, y-j); }
  1001.         while(++j < lj);
  1002.         lj = k; }
  1003. }
  1004.  
  1005. /*
  1006.  * Draw an arc centered at (x, y), radius r at vectors v1, v2
  1007.  */
  1008. arc(x, y, r, v1, v2)
  1009.     int x, y, r;
  1010.     unsigned char v1, v2;
  1011. {
  1012.     int rs, i, j, ax, x1, y1, x2, y2;
  1013.  
  1014.     x2 = -1;
  1015.     rs = r*r;
  1016.  
  1017.     do {
  1018.         j = (ARC_RES-1) - (i = v1 & (ARC_RES-1));
  1019.         switch(v1 & (ARC_RES*3)) {
  1020.             case ARC_RES*0 :    /* Quadrant one */
  1021.                 x1 = x + (ax = scale(r, sine[i], -1));
  1022.                 y1 = y - sqrt(rs - (ax*ax));
  1023.                 break;
  1024.             case ARC_RES*1 :    /* Quadrant two */
  1025.                 x1 = x + (ax = scale(r, sine[j], -1));
  1026.                 y1 = y + sqrt(rs - (ax*ax));
  1027.                 break;
  1028.             case ARC_RES*2 :    /* Quadrant three */
  1029.                 x1 = x - (ax = scale(r, sine[i], -1));
  1030.                 y1 = y + sqrt(rs - (ax*ax));
  1031.                 break;
  1032.             case ARC_RES*3 :    /* Quadrant four */
  1033.                 x1 = x - (ax = scale(r, sine[j], -1));
  1034.                 y1 = y - sqrt(rs - (ax*ax)); }
  1035.         if(x2 != -1)
  1036.             line(x2, y2, x1, y1);
  1037.         x2 = x1;
  1038.         y2 = y1; }
  1039.     while(v1++ != v2);
  1040. }
  1041.  
  1042. /*
  1043.  * Draw a text string on the screen at specified scale
  1044.  */
  1045. text(string, x, y, s)
  1046.     char *string;
  1047.     int x, y, s;
  1048. {
  1049.     unsigned i, j, b;
  1050.     unsigned char *ptr;
  1051.  
  1052.     y -= scale(24, s, 100);
  1053.     while(*string) {
  1054.         ptr = &font[(*string++ - ' ') * 48];
  1055.  
  1056.         for(i=0; i < 24; ++i) {
  1057.             b = (*ptr++ << 8) | *ptr++;
  1058.             for(j=0; j < 16; ++j) {
  1059.                 if(b & 0x8000)
  1060.                     set_pixel(x+scale(j,s,100), y+scale(i,s,100));
  1061.                 b <<= 1; } }
  1062.         x += scale(18, s, 100); }
  1063. }
  1064.  
  1065. /*
  1066.  * Clear message area at top of screen & position text cursor there
  1067.  */
  1068. message()
  1069. {
  1070.     int i;
  1071.     gotoxy(0, 0);
  1072.     for(i=0; i < 80; ++i)
  1073.         putc(' ', stdout);
  1074.     gotoxy(0, 0);
  1075. }
  1076.  
  1077. /*
  1078.  * Write title, and wait for mouse button. Return 0 if LEFT, -1 if RIGHT.
  1079.  */
  1080. wait_for_left(prompt)
  1081.     char *prompt;
  1082. {
  1083.     int s;
  1084.     message();
  1085.     printf(prompt);
  1086.     while(!((s = mouse_status()) & MOUSE_LEFT)) {
  1087.         if(s & MOUSE_RIGHT) {
  1088.             message();
  1089.             return -1; } }
  1090.     return 0;
  1091. }
  1092.  
  1093. /*
  1094.  * Write a 16 bit value to the drawing list
  1095.  */
  1096. draw(value)
  1097.     unsigned value;
  1098. {
  1099.     drawing[dtop++] = value >> 8;
  1100.     drawing[dtop++] = value & 255;
  1101. }
  1102.  
  1103. /*
  1104.  * Retrieve a 16 bit value from the drawing list
  1105.  */
  1106. dvalue()
  1107. {
  1108.     return (drawing[dpos++] << 8) | drawing[dpos++];
  1109. }
  1110.  
  1111. /*
  1112.  * Zero out the top of the drawing & set upper boundary
  1113.  */
  1114. zero_drawing(top)
  1115.     unsigned top;
  1116. {
  1117.     dtop = top;
  1118.     while(top < sizeof(drawing))
  1119.         drawing[top++] = 0;
  1120. }
  1121.  
  1122. /*
  1123.  * Prompt for, and get a numeric value from the user
  1124.  */
  1125. get_value(prompt)
  1126.     char *prompt;
  1127. {
  1128.     unsigned value;
  1129.     char buffer[51], *ptr;
  1130.  
  1131.     message();
  1132.     printf(prompt);
  1133.     fgets(ptr = buffer, sizeof(buffer)-1, stdin);
  1134.     while(isspace(*ptr))
  1135.         ++ptr;
  1136.     value = 0;
  1137.     while(isdigit(*ptr))
  1138.         value = (value*10) + (*ptr++ - '0');
  1139.     message();
  1140.     return value;
  1141. }
  1142.  
  1143. /*
  1144.  * Get a filename & access it with specified mode
  1145.  */
  1146. FILE *get_file(prompt, name, ext, mode)
  1147.     char *prompt, *name, *ext, *mode;
  1148. {
  1149.     int i, dot;
  1150.     char buffer[65], *ptr;
  1151.  
  1152.     message();
  1153.     printf("Enter %s filename (%s)?", prompt, name);
  1154.     fgets(ptr = buffer, sizeof(buffer)-1, stdin);
  1155.     while(isspace(*ptr))
  1156.         ++ptr;
  1157.     if(*ptr) {
  1158.         dot = -1;
  1159.         for(i = 0; *ptr; ++i)
  1160.             if((name[i] = *ptr++) == '.')
  1161.                 dot = i;
  1162.         name[i] = 0;
  1163.         if(dot == -1)
  1164.             while(name[i++] = *ext++); }
  1165.     message();
  1166.     return fopen(name, mode);
  1167. }
  1168.  
  1169. /*
  1170.  * Scale a value by a fraction, using a 32 bit intermediate result,
  1171.  * and round up/down to nearest integer ressult.
  1172.  */
  1173. scale(value, mul, div) asm
  1174. {
  1175.         MOV        AX,8[BP]        ; Get value
  1176.         MUL        WORD PTR 6[BP]    ; Multiply to 32 bit product
  1177.         MOV        BX,4[BP]        ; Get divisor
  1178.         DIV        BX                ; Divide back to 16 bit result
  1179.         SHR        BX,1            ; /2 for test
  1180.         JZ        scale1            ; Special case (/1)
  1181.         INC        DX                ; .5 rounds up
  1182.         SUB        BX,DX            ; Set 'C' if remainder > half
  1183.         ADC        AX,0            ; Increment result to scale
  1184. scale1:
  1185. }
  1186.  
  1187. /*
  1188.  * Initialize the video display
  1189.  */
  1190. init_video() asm
  1191. {
  1192. ; First, determine current mode for later
  1193.         MOV        AH,0Fh            ; Get video mode
  1194.         INT        10h                ; Call BIOS
  1195.         MOV        DGRP:_vmode,AL    ; Save video mode
  1196. ; Check for VGA present
  1197.         MOV        AX,1A00h        ; Get display code
  1198.         INT        10h                ; Call BIOS
  1199.         CMP        AL,1Ah            ; VGA supported?
  1200.         MOV        AL,-1            ; Assume yes
  1201.         JZ        initv1            ; It does exist
  1202.         XOR        AX,AX            ; Report failure
  1203. initv1:
  1204. }
  1205.  
  1206. /*
  1207.  * Set active video mode & clear screen
  1208.  */
  1209. video_mode(mode) asm
  1210. {
  1211.         MOV        AL,4[BP]        ; Get mode
  1212.         XOR        AH,AH            ; Func 0 - set mode
  1213.         INT        10h                ; Issue mode
  1214. }
  1215.  
  1216. /*
  1217.  * Initializes the mouse driver, returns with -1 if successful.
  1218.  */
  1219. init_mouse() asm
  1220. {
  1221. ; Initialize & test for mouse
  1222.         XOR        AX,AX            ; Init functions.
  1223.         INT        33h                ; Call mouse driver
  1224.         AND        AX,AX            ; Mouse present
  1225.         JZ        initm1            ; No, skip it
  1226. ; Set mouse limits (some drivers do not do it properly on reset)
  1227.         XOR        CX,CX            ; Lower limit is zero
  1228.         MOV        DX,639            ; Upper horizontal limit
  1229.         MOV        AX,7            ; Set horizontal limit
  1230.         INT        33h                ; Call mouse driver
  1231.         MOV        DX,479            ; Upper vertical limit
  1232.         MOV        AX,8            ; Set vertical limit
  1233.         INT        33h                ; Call mouse driver
  1234.         MOV        AX,-1            ; Indicate mouse ok
  1235. initm1:
  1236. }
  1237.  
  1238. /*
  1239.  * Set a graphic pixel, to the attribute previously set in 'pixel'
  1240.  */
  1241. set_pixel(x, y) asm
  1242. {
  1243.         MOV        DX,4[BP]        ; Get Y coordinate
  1244.         CMP        DX,VERTICAL        ; In range?
  1245.         JAE        noset            ; No skip it
  1246.         MOV        CX,6[BP]        ; Get X coordinate
  1247.         CMP        CX,HORIZONTAL    ; In Range?
  1248.         JAE        noset            ; No, skip it
  1249.         MOV        AH,0Ch            ; Write pixel function
  1250.         MOV        AL,DGRP:_pixel    ; Pixel attribute
  1251.         XOR        BH,BH            ; Zero page
  1252.         INT        10h                ; Call int 16
  1253. noset:
  1254. }
  1255.  
  1256. /*
  1257.  * Position the text cursor
  1258.  */
  1259. gotoxy(x, y) asm
  1260. {
  1261.         MOV        DH,4[BP]        ; Get Y coordinate
  1262.         MOV        DL,6[BP]        ; Get X coordinate
  1263.         XOR        BH,BH            ; Zero page
  1264.         MOV        AH,02h            ; Set cursor
  1265.         INT        10h                ; Call BIOS
  1266. }
  1267.  
  1268. /*
  1269.  * Test for a key from the console
  1270.  */
  1271. test_key() asm
  1272. {
  1273.         MOV        AH,01h            ; Test for key function
  1274.         INT        16h                ; Ask BIOS
  1275.         JNZ        getk1            ; Ready, get character
  1276.         XOR        AX,AX            ; Zero result
  1277. }
  1278.  
  1279. /*
  1280.  * Get a key without echo or editing
  1281.  */
  1282. get_key() asm
  1283. {
  1284. getk1:    XOR        AH,AH            ; Get key function
  1285.         INT        16h                ; Ask BIOS
  1286.         XOR        AH,AH            ; Zero HIGH byte
  1287. }
  1288.  
  1289. /*
  1290.  * Update mouse position and on-screen cursor. If any button is
  1291.  * activated, remove cursor, wait for button to be released, and
  1292.  * report it.
  1293.  */
  1294. mouse_status()
  1295. {
  1296.     int x, y, z;
  1297.  
  1298.     /* If no cursor on screen, draw one */
  1299.     if(!cursor_flag) {
  1300. newcursor:
  1301.         draw_cursor();
  1302.         cursor_flag = -1; }
  1303.     /* Get mouse position and button status */
  1304.     asm {
  1305.         MOV        AX,0003h    ; Mouse status function
  1306.         INT        33h            ; Call mouse driver
  1307.         MOV        -2[BP],BX    ; Save buttons
  1308.         MOV        -4[BP],DX    ; Save Y position
  1309.         MOV        -6[BP],CX    ; Save X position
  1310.         }
  1311.  
  1312.     /* If snap enabled, force cursor alignment */
  1313.     if(snap) {
  1314.         x = (x / snap) * snap;
  1315.         y = (y / snap) * snap; }
  1316.  
  1317.     /* If cursor position changed, update cursor and display*/
  1318.     if((x != mousex) || (y != mousey)) {
  1319.         draw_cursor();
  1320.         gotoxy(68, 0);
  1321.         printf("X=%-3d Y=%-3d", (mousex = x) - xbase, (mousey = y) - ybase);
  1322.         goto newcursor; }
  1323.  
  1324.     /* If any buttons are activated, wait for release, remove cursor */
  1325.     if(z & (MOUSE_LEFT|MOUSE_RIGHT|MOUSE_CENTER)) {
  1326.         asm {
  1327.             mloop1:    MOV        AX,0003h        ; Mouse status function
  1328.                     INT        33h                ; Call mouse driver
  1329.                     AND        BL,07h            ; Any buttons down?
  1330.                     JNZ        mloop1            ; Wait till clear
  1331.             }
  1332.         draw_cursor();
  1333.         cursor_flag = 0; }
  1334.  
  1335.     /* Pass back button status to caller */
  1336.     return z;
  1337. }
  1338.